#include "SelectPoll.h"

#include <stdio.h>
#include <stddef.h>
#include <string.h>

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

CSelectPoll::CSelectPoll()
    :m_iSocketCount(0)
{
}


CSelectPoll::~CSelectPoll()
{
}

bool CSelectPoll::Init(const int iReadySize)
{
    bool bFlag = CPoll::Init(iReadySize);
    if (!bFlag)
    {
        printf("ERROR Init | poll init fail. size:%d\n", iReadySize);
        return false;
    }

    
    FD_ZERO(&m_stReadFdSet);
    FD_ZERO(&m_stWriteFdSet);
    m_iMaxSocket = 0;
    m_iSocketCount = 0;
    return true;
}

bool CSelectPoll::AddReadSocket(const int iSocket)
{
    if (m_iSocketCount >= FD_SETSIZE)
    {
        printf("ERROR AddReadSocket| socket is too many. max:%d, cur_count:%d", FD_SETSIZE, m_iSocketCount);
        return false;
    }

    FD_SET(iSocket, &m_stReadFdSet);

    if (iSocket > m_iMaxSocket)
    {
        m_iMaxSocket = iSocket;
    }

    ++m_iSocketCount;
    return true;
}

void CSelectPoll::RemoveReadSocket(const int iSocket)
{
    if (!FD_ISSET(iSocket, &m_stReadFdSet))
    {
        return;
    }

    FD_CLR(iSocket, &m_stReadFdSet);

    --m_iSocketCount;
    if (iSocket == m_iMaxSocket)
    {
        m_iMaxSocket = 0;
        for (int i = iSocket; i > 0;--i)
        {
            if (FD_ISSET(i, &m_stWriteFdSet))
            {
                m_iMaxSocket = i;
                break;
            }
            if (FD_ISSET(i, &m_stReadFdSet))
            {
                m_iMaxSocket = i;
                break;
            }
        }
    }
}

bool CSelectPoll::AddWriteSocket(const int iSocket)
{
    if (m_iSocketCount >= FD_SETSIZE)
    {
        printf("ERROR AddWriteSocket| socket is too many.  max:%d, cur_count:%d", FD_SETSIZE, m_iSocketCount);
        return false;
    }

    FD_SET(iSocket, &m_stWriteFdSet);

    if (iSocket > m_iMaxSocket)
    {
        m_iMaxSocket = iSocket;
    }


    ++m_iSocketCount;
    return true;

}

void CSelectPoll::RemoveWriteSocket(const int iSocket)
{
    FD_CLR(iSocket, &m_stWriteFdSet);
    --m_iSocketCount;

    if (iSocket == m_iMaxSocket)
    {
        m_iMaxSocket = 0;
        for (int i = iSocket; i > 0;--i)
        {
            if (FD_ISSET(i, &m_stWriteFdSet))
            {
                m_iMaxSocket = i;
                break;
            }
            if (FD_ISSET(i, &m_stReadFdSet))
            {
                m_iMaxSocket = i;
                break;
            }
        }
    }
}

int CSelectPoll::Polling(int iWaitTime)
{
    struct timeval stWaitTime;
    struct timeval *pWaitTime;

    if (iWaitTime <= -1)
    {
        pWaitTime = NULL;
    }
    else
    {
        stWaitTime.tv_sec = iWaitTime / 1000000;
        stWaitTime.tv_usec = iWaitTime % 1000000;
        pWaitTime = &stWaitTime;
    }

    fd_set stReadFdSet;
    fd_set stWriteFdSet;

    memcpy(&stReadFdSet, &m_stReadFdSet, sizeof(stReadFdSet));
    memcpy(&stWriteFdSet, &m_stWriteFdSet, sizeof(stWriteFdSet));

    int iCount = select(m_iMaxSocket+1, &stReadFdSet, &stWriteFdSet, NULL, pWaitTime);
    if (iCount > 0)
    {
        for (int i = 0; i <= m_iMaxSocket; ++i)
        {
            if (FD_ISSET(i, &stWriteFdSet))
            {
                AddReadySocket(i, ENM_SOCKET_READY_EVENT_TYPE_WRITE);
                RemoveReadSocket(i);
            }
            if (FD_ISSET(i, &stReadFdSet))
            {
                AddReadySocket(i, ENM_SOCKET_READY_EVENT_TYPE_READ);
                RemoveWriteSocket(i);
            }
        }
    }

    return iCount;
}

const char * CSelectPoll::GetName()
{
    return "select";
}
